﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Text;
using System.Drawing;
using System.Collections;
using System.ComponentModel;

namespace ICSharpCode.WinFormsUI.Controls
{
    public class NListBox : NPanel
    {
        private int _lastSelectedIndex = -1;
        private int _lastHoverIndex = -1;
        private Padding padding = new Padding(2, 2, 2, 2);
        private StringFormat stringFormat = new StringFormat();        

        private NListItemCollection _items;
        public NListItemCollection Items 
        {
            get { return _items; }
            set
            {
                _items = value;
                //SetScrollBars();
            }
        }

        protected override Padding DefaultPadding
        {
            get
            {
                return padding;
            }
        }

        private int _selectedIndex;        
        public int SelectedIndex
        {
            get { return _selectedIndex; }
            set
            {
                _selectedIndex = value;
                this.Invalidate();
            }
        }

        private object _selectedItem;
        public object SelectedItem
        {
            get
            {
                if (_selectedIndex != -1)
                {
                    if (_items.Count > _selectedIndex)
                    {
                        _selectedItem = this._items[_selectedIndex];
                    }
                }
                return _selectedItem;
            }
            set
            {
                if (_selectedItem != value)
                {
                    _selectedItem = value;
                    OnSelectedIndexChanged(new EventArgs());
                }
                else
                {
                    _selectedItem = value;
                }              
            }
        }

        private DrawMode _drawMode;
        public DrawMode DrawMode
        {
            get { return _drawMode; }
            set { _drawMode = value; }
        }

        private int _itemHeight = 18;
        public int ItemHeight
        {
            get { return _itemHeight; }
            set { _itemHeight = value; }
        }

        private bool _multiColumn;
        public bool MultiColumn
        {
            get { return _multiColumn; }
            set
            {
                _multiColumn = value;
                OnMultiColumnChanged(value);
            }
        }

        private string _text = "";
        public new string Text
        {
            get
            {
                if (_selectedIndex > -1 &&
                    _items != null &&
                    _selectedIndex < _items.Count)
                    return _items[_selectedIndex].ToString();
                return _text;
            }
            set { _text = value; }
        }

        private bool _isHoverStyle = false;
        public bool IsHoverStyle
        {
            get { return _isHoverStyle; }
            set { _isHoverStyle = value; }
        }

        private TextRenderModel _textRender = TextRenderModel.Default;
        public TextRenderModel TextRender
        {
            get { return _textRender; }
            set
            {
                _textRender = value;
                this.Invalidate();
            }
        }

        private NScrollBar hScrollBar = new NHScrollBar();
        public NScrollBar HScrollBar
        {
            get { return hScrollBar; }
        }

        private NScrollBar vScrollBar = new NVScrollBar();
        public NScrollBar VScrollBar
        {
            get { return vScrollBar; }
        }

        protected Point VisiableTop = new Point();

        public event EventHandler SizeChanged;

        public event EventHandler SelectedIndexChanged;

        public event EventHandler ItemClicked;

        public event NDrawItemEventHandler DrawItem;

        public NListBox()
        {
            InitializeControl();           
        }

        public void InitializeControl()
        { 
            this.DoubleBuffered = true;
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer |
             ControlStyles.ResizeRedraw |
             ControlStyles.AllPaintingInWmPaint, true);
            this.UpdateStyles();

            Control.CheckForIllegalCrossThreadCalls = false;

            Items = new NListItemCollection(this);
            BorderStyle = NBorderStyle.All;
            stringFormat = new StringFormat() { LineAlignment = StringAlignment.Center };

            AddScrollBars();
            //UpdateScrollBars();          
        }

        protected void AddScrollBars()
        {
            vScrollBar.Visible = false;
            hScrollBar.Visible = false;

            vScrollBar.DisabledProcessDialogKey = true;
            hScrollBar.DisabledProcessDialogKey = true;

            vScrollBar.Width = ScrollBarInformation.VerticalScrollBarWidth;
            hScrollBar.Height = ScrollBarInformation.HorizontalScrollBarHeight;

            vScrollBar.ValueChanged += VScrollBar_ValueChanged;
            hScrollBar.ValueChanged += HScrollBar_ValueChanged;

            vScrollBar.ProcessKey += VScrollBar_ProcessKey;

            this.Controls.Add(vScrollBar);
            this.Controls.Add(hScrollBar);
        }

        protected void DrawHorizontalItems(Graphics g)
        {
            if (this.Items != null)
            {
                for (int i = 0; i < this.Items.Count; i++)
                {
                    DrawListBoxItem(g, i);
                }
            }
        }

        protected void DrawVerticalItems(Graphics g)
        {
            if (this.Items != null)
            {
                for (int i = 0; i < this.Items.Count; i++)
                {
                    DrawListBoxItem(g, i);
                }
            }
        }

        protected void SetScrollBarsInfo()
        {
            int borderWidth = 1;
            Rectangle rect = new Rectangle(padding.Left, padding.Top, this.Width - padding.Left - padding.Right, this.Height - padding.Top - padding.Bottom);

            SizeF Maxmum = MeasureScrollMaxmum();
            VScrollBar.SmallChange = ItemHeight;
            VScrollBar.LargeChange = rect.Height;
            VScrollBar.Maximum = (int)Maxmum.Width > rect.Height ? (int)Maxmum.Width - rect.Height : rect.Height;

            vScrollBar.Visible = false;
            if (Maxmum.Width > rect.Height)
            {
                vScrollBar.Visible = true;
            }

            hScrollBar.Visible = false;
            hScrollBar.Maximum = (int)Maxmum.Height;

            vScrollBar.SetBounds(this.Width - vScrollBar.Width - borderWidth, borderWidth, vScrollBar.Width, hScrollBar.Visible ? this.Height - hScrollBar.Height - 2 * borderWidth : this.Height - 2 * borderWidth);
            hScrollBar.SetBounds(borderWidth, this.Height - hScrollBar.Height - borderWidth,
                vScrollBar.Visible ? this.Width - vScrollBar.Width - 2 * borderWidth : this.Width - 2 * borderWidth, this.Height - hScrollBar.Height - borderWidth);
        }

        protected virtual void OnMultiColumnChanged(bool value)
        {
            stringFormat = new StringFormat();
            if (!value)
            {
                stringFormat.LineAlignment = StringAlignment.Center;
            }
            else
            {
                stringFormat.Alignment = StringAlignment.Center;
            }
        }

        protected virtual void OnDrawItem(NDrawItemEventArgs e)
        {
            if (this.DrawItem != null)
            {
                this.DrawItem(this, e);
            }
            else
            {                
                e.DrawBackground();
                e.DrawFocusRectangle();
                e.DrawText();
            }
        }

        protected virtual void OnSelectedIndexChanged(EventArgs e)
        {
            if (this.SelectedIndexChanged != null)
            {
                this.SelectedIndexChanged(this, e);
            }
            if (this.Items.Count > 0)
            {
                if (_selectedIndex > -1)
                    if (this.Items.Count > _selectedIndex)
                        this._text = this.Items[_selectedIndex].ToString();
            }
        }

        protected virtual bool DoProcessDialogKey(Keys keyData)
        {
            Keys keyUp = Keys.Up;
            Keys keyDown = Keys.Down;

            if (keyData == keyUp)
            {
                if (VScrollBar.Visible)
                    VScrollBar.Value -= VScrollBar.SmallChange;
                if (this.SelectedIndex > 0)
                {
                    this.SelectedIndex -= 1;
                }
            }

            if (keyData == keyDown)
            {
                if (VScrollBar.Visible)
                    VScrollBar.Value += VScrollBar.SmallChange;
                if (this.SelectedIndex < this.Items.Count - 1)
                {
                    this.SelectedIndex += 1;
                }
            }

            if (keyData == Keys.Home)
            {
                if (VScrollBar.Visible)
                    VScrollBar.Value = VScrollBar.Minimum;
                this.SelectedIndex = 0;
            }

            if (keyData == Keys.End)
            {
                if (VScrollBar.Visible)
                    VScrollBar.Value = VScrollBar.Maximum;
                this.SelectedIndex = this.Items.Count - 1;
            }

            if (keyData == Keys.Enter)
            {
                if (ItemClicked != null)
                    ItemClicked(this, new EventArgs());                
            }

            if (_lastSelectedIndex != this.SelectedIndex &&
                    SelectedIndexChanged != null)
                SelectedIndexChanged(this, new EventArgs());

            _lastSelectedIndex = this.SelectedIndex;

            return false;
        }

        private Rectangle MeasureItemRectangle(int Index)
        {
            Rectangle rect = Rectangle.Empty;

            int top = VisiableTop.Y + Index * this.ItemHeight + padding.Top;
            int left = VisiableTop.X + padding.Left;
            int width = this.Width - padding.Left - padding.Right;
            int height = this.Height - padding.Top - padding.Bottom;

            if (MultiColumn)
            {
                rect = new Rectangle(left, top, ItemHeight, height);
            }
            else
            {
                rect = new Rectangle(left, top, width, ItemHeight);
            }           

            return rect;
        }

        private SizeF MeasureScrollMaxmum()
        {
            float VMaxmum = 0;
            float HMaxumn = 0;
            if (this.Items != null)
            {
                int width = this.Width - padding.Left - padding.Right;
                using (Graphics g = this.CreateGraphics())
                {
                    foreach (object item in Items)
                    {
                        VMaxmum += ItemHeight;
                        HMaxumn = Math.Max(width, g.MeasureString(item.ToString(), this.Font).Width);
                    }
                }
                VMaxmum += ItemHeight;
            }
            return new SizeF(VMaxmum, HMaxumn);
        }

        private void DrawListBoxItem(Graphics g, int index)
        {
            Color foreColor = this.ForeColor;
            Color backColor = this.BackColor;
            DrawItemState state = DrawItemState.None;

            if (_isHoverStyle &&
                _lastHoverIndex != -1 && 
                _lastHoverIndex == index)
            {
                foreColor = Color.Black;
                backColor = Color.FromArgb(240, 240, 240);
                state = DrawItemState.Selected;
            }
            if (this._selectedIndex == index)
            {
                foreColor = Color.White;
                backColor = Color.FromArgb(051, 153, 255);
                state = DrawItemState.Selected;
            }

            string itemText = this.Items[index].ToString();
            Rectangle rect = MeasureItemRectangle(index);
            switch (_textRender)
            {
                case TextRenderModel.Single:
                    OnDrawItem(new NDrawItemEventArgs(g, this.Font, rect, index, state, foreColor, backColor, itemText, stringFormat, TextFormatFlags.SingleLine | TextFormatFlags.WordBreak | TextFormatFlags.VerticalCenter));
                    break;
                case TextRenderModel.Default:
                    OnDrawItem(new NDrawItemEventArgs(g, this.Font, rect, index, state, foreColor, backColor, itemText, stringFormat, TextFormatFlags.Default | TextFormatFlags.VerticalCenter));
                    break;
            }
        }

        private void OnVScroll(object sender, ScrollEventArgs e)
        {
            int m_vScroll_pos = VisiableTop.Y - (e.NewValue - e.OldValue);
            VisiableTop = new Point(VisiableTop.X, m_vScroll_pos);
            this.Invalidate();
        }

        private bool VScrollBar_ProcessKey(NScrollBar sender, Keys key)
        {
            return DoProcessDialogKey(key);
        }

        private void HScrollBar_ValueChanged(object sender, EventArgs e)
        {
            throw new NotImplementedException();
        }

        private void VScrollBar_ValueChanged(object sender, EventArgs e)
        {
            this.VisiableTop = new Point(VisiableTop.X, -VScrollBar.Value);
            this.Invalidate();
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            if (MultiColumn)
            {
                DrawHorizontalItems(e.Graphics);
            }
            else
            {
                DrawVerticalItems(e.Graphics);
            }

            base.OnPaint(e);
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
            if (this.Items != null)
            {
                _selectedIndex = -1;
                for (int i = 0; i < this.Items.Count; i++)
                {
                    Rectangle rect = MeasureItemRectangle(i);
                    if (rect.Contains(e.Location))
                    {
                        _selectedIndex = i;
                        break;
                    }
                }
                if (_selectedIndex > -1)
                {                    
                    if(_selectedIndex!=_lastSelectedIndex)
                        OnSelectedIndexChanged(new EventArgs());

                    this.SelectedIndex = _selectedIndex;
                    _lastSelectedIndex = _selectedIndex;

                    if (ItemClicked != null)
                        ItemClicked(this, new EventArgs());
                }                
            }
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if (_isHoverStyle && this.Items != null)
            {
                int _hoverIndex = -1;
                for (int index = 0; index < this.Items.Count; index++)
                {
                    var item = this.Items[index];
                    Rectangle rectangle = MeasureItemRectangle(index);
                    if (rectangle.Contains(e.Location))
                    {
                        _hoverIndex = index;
                        break;
                    }
                }
                if (_hoverIndex != _lastHoverIndex)
                {
                    _lastHoverIndex = _hoverIndex;
                    this.Invalidate();
                }                
            }
        }

        //protected override void OnMouseDoubleClick(MouseEventArgs e)
        //{
        //    base.OnMouseDoubleClick(e);
        //    if (this.Items != null)
        //    {
        //        _selectedIndex = -1;
        //        for (int i = 0; i < this.Items.Count; i++)
        //        {
        //            Rectangle rect = MeasureItemRectangle(i);
        //            if (rect.Contains(e.Location))
        //            {
        //                _selectedIndex = i;
        //                break;
        //            }
        //        }
        //        if (_selectedIndex > -1)
        //        {
        //            this.SelectedIndex = _selectedIndex;
        //        }
        //    }
        //}

        protected override void OnSizeChanged(EventArgs e)
        {
            base.OnSizeChanged(e);
            if (SizeChanged != null)
            {
                SizeChanged(this, new EventArgs());
            }
        }

        protected override void OnMouseWheel(MouseEventArgs e)
        {
            base.OnMouseWheel(e);

            if (!this.vScrollBar.Visible)
                return;

            int num1 = -e.Delta / NativeMethods.WHEEL_DELTA * this.ItemHeight;

            int oldValue = this.vScrollBar.Value;
            int newValue = this.vScrollBar.Value + num1;

            if (newValue < this.vScrollBar.Minimum)
                newValue = this.vScrollBar.Minimum;

            if (newValue > this.vScrollBar.Maximum)
                newValue = this.vScrollBar.Maximum;

            OnVScroll(this, new ScrollEventArgs(ScrollEventType.SmallDecrement, oldValue, newValue));

            this.vScrollBar.Value = newValue;
        }

        protected override bool ProcessDialogKey(Keys keyData)
        {
            return DoProcessDialogKey(keyData);
        }

        public void UpdateScrollBars()
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new MethodInvoker(delegate ()
                {
                    SetScrollBarsInfo();
                }));                
            }
            else
            {
                SetScrollBarsInfo();
            }            
        }

        public object FindItemAt(Point location)
        {
            object result = null;
            for (int i = 0; i < this.Items.Count; i++)
            {
                Rectangle rect = MeasureItemRectangle(i);
                if (rect.Contains(location))
                {
                    result = this.Items[i];
                    break;
                }
            }
            return result;
        }

        public void SetTheme(string theme)
        {
            Color listBoxBackColor = Color.White;
            Color listBoxForeColor = Color.Black;

            switch (theme)
            {
                case "Default":

                    vScrollBar.BorderColor = SystemColors.ControlLight;
                    hScrollBar.BorderColor = SystemColors.ControlLight;
                    vScrollBar.ScrollBarRenderer = new WhiteScrollBarRenderer();
                    hScrollBar.ScrollBarRenderer = new WhiteScrollBarRenderer();

                    listBoxBackColor = Color.White;
                    listBoxForeColor = Color.Black;

                    break;
                case "Black":

                    vScrollBar.BorderColor = Color.FromArgb(93, 140, 201);
                    hScrollBar.BorderColor = Color.FromArgb(93, 140, 201);
                    vScrollBar.ScrollBarRenderer = new BlackScrollBarRenderer();
                    hScrollBar.ScrollBarRenderer = new BlackScrollBarRenderer();

                    listBoxForeColor = Color.FromArgb(240, 240, 240);
                    listBoxBackColor = Color.FromArgb(030, 030, 030);

                    break;
            }

            this.ForeColor = listBoxForeColor;
            this.BackColor = listBoxBackColor;

        }

    } 
    
    public enum TextRenderModel
    {
        Single,
        Default
    }
}
